/*
 * asteriskaudio.cxx
 *
 * Sound driver implementation for ASTERISK PBX.
 * OpenH323 Wrapper Library.
 *
 * Copyright (c) 2002-2005 InAccess Networks
 * Michalis Manousos <manousos@inaccessnetworks.com>
 * Dimitris Economou <decon@inaccessnetworks.com>
 *
 * This file is part of "H.323 support for ASTERISK"
 *
 * "H.323 support for ASTERISK" is free software;
 * you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or (at your option) any later version. 
 *
 * "H.323 support for ASTERISK" is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. 
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 * $Id: asteriskaudio.cxx,v 1.2 2010/05/04 09:02:57 mmanousos Exp $
 *
 */

#include <ptlib.h>
#include <sys/time.h>
#include <rtp.h>

#include "oh323.h"
#include "version.h"
#include "wrapper_misc.hxx"
#include "asteriskaudio.hxx"


/***********************************************************
 * Class PAsteriskAudioDelay
 **********************************************************/

PAsteriskAudioDelay::PAsteriskAudioDelay()
{
	WRAPTRACE(4, "Object initialized.");

	firstTime = TRUE;
	error = 0;
}
    
PAsteriskAudioDelay::~PAsteriskAudioDelay()
{
	WRAPTRACE(4, "Object deleted.");
}
    
void PAsteriskAudioDelay::Restart()
{
	WRAPTRACE(4, "Restart.");

	firstTime = TRUE;
	//error = 0;
}
  
BOOL PAsteriskAudioDelay::ReadDelay(int frameTime)
{
  if (firstTime) {
    firstTime = FALSE;
    targetTime = PTime();   // targetTime is the time we want to delay to
    return TRUE;
  }

  // Set the new target
  targetTime += frameTime;

  // Calculate the sleep time so we delay until the target time
  PTimeInterval delay = targetTime - PTime();
  int sleep_time = (int)delay.GetMilliSeconds();

  if (sleep_time > 0)
    usleep(sleep_time * 1000);

  return sleep_time <= -frameTime;
}
BOOL PAsteriskAudioDelay::WriteDelay(int frameTime)
{
	if (firstTime) {
		firstTime = FALSE;
		targetTime = PTime();
		error = 0;
		return TRUE;
	}

	targetTime += frameTime;
	targetTime += error;		// Decrease it

	PTimeInterval delay = targetTime - PTime();
	int sleep_time = (int)delay.GetMilliSeconds();
	if (sleep_time <= 0) {
		WRAPTRACE(3, "Schedule in the past???");
		error = sleep_time;
	} else {
		error = 0;
		usleep(sleep_time * 1000);
	}

	return error <= -frameTime;
}
BOOL PAsteriskAudioDelay::Delay(int frameTime)
{
	if (firstTime) {
		firstTime = FALSE;
		previousTime = PTime();
		return TRUE;
	}

	error += frameTime;

	PTime now;
	PTimeInterval delay = now - previousTime;
	error -= (int)delay.GetMilliSeconds();
	previousTime = now;

	if (error > 0)
		usleep(error * 1000);

	return error <= -frameTime;
}

/***********************************************************
 * Class PAsteriskSoundChannel
 **********************************************************/

PAsteriskSoundChannel::PAsteriskSoundChannel()
{
	WRAPTRACE(4, "Object initialized.");
	mutex_flag = 0;
	lastWritten = 0;
	frameTime = 0;
	frameNum = 0;
	time_diff = 0;
	first_write = TRUE;
	//SetReadTimeout(0);
	//astWriteDelay.Delay(0);
	//astWriteDelay.ReadDelay(0);
	astReadDelay.ReadDelay(0);
	writeCount = 0;
	readCount = 0;
	shortWriteCount = 0;
	readBufIndex = 0;
	readBufSize = 0;

	Construct();
}

void PAsteriskSoundChannel::Construct()
{
}

PAsteriskSoundChannel::~PAsteriskSoundChannel()
{
	Close();
	WRAPTRACE(3, "Total I/Os: read=" << readCount << ", write=" << writeCount);
	WRAPTRACE(3, "Short I/Os: write=" << shortWriteCount);
	WRAPTRACE(4, "Object deleted.");
	baseChannel = NULL;
}

PStringArray PAsteriskSoundChannel::GetDeviceNames(Directions dir)
{
	PStringArray	recordArray, playArray;
	PStringArray	error;
	int				i;
	char			buf[MAX_FIFO_NAME];

	error[0] = "";

	/* Fill the recordArray */
	for (i=0; i<AST_OH323_MAX_CALLS; i++) {
		memset(buf, 0, MAX_FIFO_NAME);
		snprintf(buf, MAX_FIFO_NAME-1, "%sin%d", AST_OH323_FIFO_PREFIX, i);
		recordArray[i] = buf;
	}

	/* Fill the playArray */
	for (i=0; i<AST_OH323_MAX_CALLS; i++) {
		memset(buf, 0, MAX_FIFO_NAME);
		snprintf(buf, MAX_FIFO_NAME-1, "%sout%d", AST_OH323_FIFO_PREFIX, i);
		playArray[i] = buf;
	}

	if (dir == Recorder) {
		return recordArray;
	} else if (dir == Player) {
		return playArray;
	} else {
		return error;
	}
}

PString PAsteriskSoundChannel::GetDefaultDevice(Directions dir)
{
	PString		res = AST_OH323_FIFO_PREFIX;

	if (dir == Recorder) {
		res += "in0";
		return res;
	} else if (dir == Player) {
		res += "out0";
		return res;
	} else {
		return "";
	}
}

BOOL PAsteriskSoundChannel::Open(const PString & device,
								 int deviceFd,
								 Directions dir,
								 unsigned numChannels,
								 unsigned mediaFmt,
								 unsigned frameTm,
								 unsigned frameNm,
								 unsigned packetSz)
{
	Close();

	/***********
	if (!ConvertOSError(os_handle = ::open(device, dir == Player ? O_WRONLY : O_RDONLY | O_NONBLOCK)))
		return FALSE;
	**********/
	if (deviceFd < 0)
		return FALSE;
	baseChannel = this;  // XXX Use the old interface of the PSoundChannel
	os_handle = deviceFd;
	mediaFormat = mediaFmt;
	frameTime = frameTm;
	frameNum = frameNm;
	packetSize = packetSz;
	WRAPTRACE(3, "os_handle " << os_handle << ", mediaFormat " << mediaFormat
				<< ", frameTime " << frameTime << " ms, frameNum " << frameNum
				<< ", packetSize " << packetSize);
	return TRUE;
}

BOOL PAsteriskSoundChannel::Close()
{
	// XXX Do not close it from in here, do it from the channel driver.
//	os_handle = -1;
//	return TRUE;

	if (os_handle < 0)
		return TRUE;
		
	WRAPTRACE(3, "Closing os_handle " << os_handle);
	if (PChannel::Close() != TRUE) {
		return FALSE;
	} else {
		os_handle = -1;
		return TRUE;
	}
}

BOOL PAsteriskSoundChannel::SetFormat(unsigned numChannels,
							  unsigned sampleRate,
							  unsigned bitsPerSample)
{
	PAssert(numChannels >= 1 && numChannels <= 2, PInvalidParameter);
	PAssert(bitsPerSample == 8 || bitsPerSample == 16, PInvalidParameter);
	return TRUE;
}

BOOL PAsteriskSoundChannel::SetBuffers(PINDEX size, PINDEX count)
{
	PAssert(size > 0 && count > 0 && count < 65536, PInvalidParameter);
	bufSize = size;
	bufCount = count;

	return TRUE;
}

BOOL PAsteriskSoundChannel::GetBuffers(PINDEX & size, PINDEX & count)
{
	size = bufSize;
	count = bufCount;

	return TRUE;
}

static unsigned const G7231FrameSize[4] = { 24, 20, 4, 1};
BOOL PAsteriskSoundChannel::Write(const void * buffer, PINDEX length)
{
	//int written, size, num, writeSize;
	int num, writeSize;
	unsigned fsize;
	char *buf;

	if (length < 0) {
		WRAPTRACE(3, "Request to write " << length << " bytes!");
	}

	if (os_handle < 0) {
		WRAPTRACE(3, "Channel is shutting down!");
		return FALSE;
	}

/************
	if (first_write == TRUE) {
		PTime time1;
		previous_write = time1;
		time_diff = 0;
		first_write = FALSE;
	}
************/
	// Do a sleep to simulate the amount of time the hardware would take
	// to write out this data.
	lastWritten = 0;
	switch (mediaFormat) {
		//--------------- G.711 --------------------
		case RTP_DataFrame::PCMU:
		case RTP_DataFrame::PCMA:
			if ((unsigned)length < packetSize) {
				//WRAPTRACE(2, "Short write G.711 - " << length << "/" << packetSize);
				// XXX Special case for G.711: The remote EP sends smaller
				// frames. Update our defaults (We only decrease our values)
				packetSize = length;
				frameNum = length/8;
				//cout << "-- New packetSize is " << packetSize << endl;
			}
			writeSize = packetSize;
			num = frameNum;
			if (PChannel::Write(buffer, writeSize) != TRUE) {
				WRAPTRACE(2, "Write failed (G.711) - " << GetErrorText());
				return FALSE;
			} else {
				// 1 msec/frame
				//num = writeSize/8;
				// XXX Special case for G.711 (see codecs.cxx and lid.cxx)
				astWriteDelay.ReadDelay(num * frameTime);
				lastWritten = writeSize;
				++writeCount;
			}
			break;
		//--------------- G.726 --------------------
		case RTP_DataFrame::G726:
			if (length == 0) {
				// Nothing to write, just wait and return
				astWriteDelay.ReadDelay(frameNum * frameTime);
				lastWritten = 0;
				++writeCount;
				break;
			}
			if ((unsigned)length < packetSize) {
				WRAPTRACE(2, "Short write G.726 - " << length << "/" << packetSize);
				lastWritten = length;
				++shortWriteCount;
				return TRUE;
			} else {
				writeSize = packetSize;
			}
			num = frameNum;
			if (PChannel::Write(buffer, writeSize) != TRUE) {
				WRAPTRACE(2, "Write failed (G.726) - " << GetErrorText());
				return FALSE;
			} else {
				// 1 msec/frame
				astWriteDelay.ReadDelay(num * frameTime);
				lastWritten = writeSize;
				++writeCount;
			}
			break;
		//--------------- G.729 --------------------
		case RTP_DataFrame::G729:
			/* XXX Not complete... */
			if ((unsigned)length < packetSize) {
				WRAPTRACE(2, "Short write G.729 - " << length << "/" << packetSize);
				lastWritten = length;
				++shortWriteCount;
				return TRUE;
			} else {
				writeSize = packetSize;
			}
			if (PChannel::Write(buffer, writeSize) != TRUE) {
				WRAPTRACE(2, "Write failed (G.729) - " << GetErrorText());
				return FALSE;
			} else {
				// 10 msec/frame
				astWriteDelay.ReadDelay(frameTime);
				lastWritten = writeSize;
				++writeCount;
			}
			break;
		//--------------- GSM 06.10 --------------------
		case RTP_DataFrame::GSM:
			if ((unsigned)length < packetSize) {
				WRAPTRACE(2, "Short write GSM - " << length << "/" << packetSize);
				lastWritten = length;
				++shortWriteCount;
				return TRUE;
			} else {
				writeSize = packetSize;
			}
			if (PChannel::Write(buffer, writeSize) != TRUE) {
				WRAPTRACE(2, "Write failed (GSM) - " << GetErrorText());
				return FALSE;
			} else {
				// 20 msec/frame
				//num = length/33;
				astWriteDelay.ReadDelay(frameTime);
				lastWritten = writeSize;
				++writeCount;
			}
			break;
		//--------------- G.723.1 --------------------
		case RTP_DataFrame::G7231:
			buf = (char*)buffer;
			fsize = G7231FrameSize[buf[0]&3];
			if ((unsigned)length < fsize) {
				WRAPTRACE(2, "Short write G.723.1 - " << length << "/" << fsize);
				// This is error for G.723.1
				return FALSE;
			} else {
				writeSize = fsize;
			}
			if (PChannel::Write(buffer, writeSize) != TRUE) {
				WRAPTRACE(2, "Write failed (G.723.1) - " << GetErrorText());
				return FALSE;
			} else {
				// 30 msec/frame
				astWriteDelay.ReadDelay(frameTime);
				lastWritten = writeSize;
				++writeCount;
			}
/***************
			buf = (char*)buffer;
			size = length;
			while (size > 0) {
				written = G7231FrameSize[buf[length-size]&3];
				if (written != 1) {
					if (PChannel::Write((const void *)&buf[length-size], written) != TRUE) {
						WRAPTRACE(2, "Write failed (G.723.1) - " << GetErrorText());
						return FALSE;
					} else {
						// 30 msec/frame
						astWriteDelay.Delay(frameTime);
					}
				} else {	// written == 1 (Silence)
					// 30 msec/frame
					astWriteDelay.Delay(frameTime);
				}
				size -= written;
				lastWritten += written;
			}
*******************/
			break;
		default:
			WRAPTRACE(2, "Unknown media format " << mediaFormat);
			break;
	}

/*************
	PTime time2;
	time_diff = time2 - previous_write;
	previous_write = time2;
	WRAPTRACE(2, "Written [" << GetLastWriteCount() <<" bytes] [" << 
			time_diff.GetMilliSeconds() << " ms]");
************/
	WRAPTRACE(5, "Written [" << GetLastWriteCount() <<" bytes]");
	return TRUE;
}

PINDEX PAsteriskSoundChannel::GetLastWriteCount() const
{
	return(lastWritten < 0 ? 0 : lastWritten);
}

PINDEX PAsteriskSoundChannel::GetLastReadCount() const
{
	return(lastReadCount < 0 ? 0 : lastReadCount);
}

BOOL PAsteriskSoundChannel::Read(void * buffer, PINDEX length)
{
	int readTimeout = -1;
	int readsize;

	if (os_handle < 0) {
		WRAPTRACE(3, "Channel is shutting down!");
		return FALSE;
	}

	if (readBufSize == 0) {
		// Read as much data as possible, if our buffer is empty
		SetReadTimeout(0);
		lastReadCount = 0;
		readBufIndex = 0;
		readsize = (sizeof(readBuffer) / packetSize) * packetSize;
		if (PChannel::Read(readBuffer, readsize) != TRUE) {
			if (GetErrorCode(LastReadError) == PChannel::Timeout) {
				WRAPTRACE(4, "Timeout [" << GetLastReadCount() << " bytes]");
			} else if (GetErrorCode(LastReadError) != PChannel::Interrupted) {
				WRAPTRACE(2, "Failure - " << GetErrorText());
				return FALSE;
			}
		} else {
			WRAPTRACE(5, "Data read [" << GetLastReadCount() << " bytes]");
			readBufSize = GetLastReadCount();
			// XXX Check whether we are staying behind and drop 
			//     excessive data.
			if (readBufSize > 20 * packetSize) {
				WRAPTRACE(2, "Too many data from application (" << readBufSize 
							<< " bytes). Discarding them.");
				readBufSize = 2 * packetSize;
			}
		}
	}

	switch (mediaFormat) {
		case RTP_DataFrame::PCMU:
		case RTP_DataFrame::PCMA:
			// 1 msec/frame * txFrames
			// XXX Special case for G.711 (see codecs.cxx and lid.cxx)
			//readTimeout = frameTime * (length/8);
			readTimeout = frameNum * frameTime;
			break;
		case RTP_DataFrame::G726:
			readTimeout = frameNum * frameTime;
			break;
		case RTP_DataFrame::G729:
			// 10 msec/frame
		case RTP_DataFrame::GSM:
			// 20 msec/frame
		case RTP_DataFrame::G7231:
			// 30 msec/frame
			readTimeout = frameTime;
			//SetReadTimeout(frameTime);
			break;
		default:
			WRAPTRACE(2, "Unknown media format " << mediaFormat);
			break;
	}

	if (readTimeout > 0) {
		astReadDelay.ReadDelay(readTimeout);
	} else
		return FALSE;

	// XXX Fix this for variable-size frame codecs!!!
	if (readBufSize >= (unsigned)length) {
		memcpy((char *)buffer, (char *)(readBuffer + readBufIndex), length);
		lastReadCount = length;
		readBufIndex += length;
		readBufSize -= length;
	} else {
		lastReadCount = 0;
	}
	++readCount;

	return TRUE;
}

// End of file /////////////////////////////////////////////////////////////
